package com.limit.aspect.core;

import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @title: AnnotationAspectBeanPostProcessor
 * @Author xu-xiang
 * @Date: 2024/3/26 15:50
 * @description:
 */
public class AnnotationAspectBeanPostProcessor implements BeanPostProcessor {

    private static final String POINTCUT_EXPRESSION_PATTERN = "\\(([^\\)]+)\\)"; //切入点表达式正则

    private static final Logger logger = LoggerFactory.getLogger(AnnotationAspectBeanPostProcessor.class);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        if (bean instanceof AnnotationAspectTemplate) {
            AnnotationAspectTemplate template = (AnnotationAspectTemplate) bean;

            Method pointcutMethod;
            try {
                pointcutMethod = template.getClass().getMethod("pointcut");
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
            Pointcut pointcut = AnnotationUtils.getAnnotation(pointcutMethod, Pointcut.class);
            Assert.notNull(pointcut, bean.getClass() + "must declare pointcut");

            Class<? extends Annotation> annotationType = template.pointcutAnnotationClass();
            Assert.notNull(annotationType, "pointcutAnnotationClass must be not null");

            String pointcutExpression = pointcut.value(); //切入点表达式
            Pattern pattern = Pattern.compile(POINTCUT_EXPRESSION_PATTERN);
            Matcher matcher = pattern.matcher(pointcutExpression);

            Set<String> classSet = new HashSet<>(2);
            while (matcher.find()) {
                String className = matcher.group(1);
                classSet.add(className.trim());
            }

            if (classSet.isEmpty()) {
                logger.error("切入点表达式:{}错误", pointcutExpression);
                throw new RuntimeException(bean.getClass() + "must declare pointcut");
            } else if (classSet.size() > 1) {
                logger.error("切入点表达式:{}错误", pointcutExpression);
                throw new RuntimeException("请针对一个注解进行切入(职责单一)");
            } else if (!classSet.contains(annotationType.getName())) {
                logger.error("获取注解信息失败, 请检查类:{} 的切入点表达式是否为:{}.class", getClass(), annotationType.getName());
                throw new NullPointerException("获取注解信息失败, 请确保pointcut(), pointcutAnnotationClass()作用于同一个注解class");
            }
        }

        return bean;
    }
}
